/*
 * Created on Jun 4, 2009
 *
 * To change the template for this generated file go to
 * Window&gt;Preferences&gt;Java&gt;Code Generation&gt;Code and Comments
 */
package ro.qi.framework.sys.mail;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.Vector;

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import ro.qi.framework.sys.debug.Logger;
import ro.qi.framework.sys.mail.entities.MailAttachment;
import ro.qi.framework.sys.mail.entities.MailMessage;
import ro.qi.framework.sys.mail.entities.MailMessageAttachmentCollection;
import ro.qi.framework.sys.mail.entities.MailMessageCollection;
import ro.qi.framework.sys.mail.entities.MailParameter;

/**
 * @author George Pogorelschi
 *
 * To change the template for this generated type comment go to
 * Window&gt;Preferences&gt;Java&gt;Code Generation&gt;Code and Comments
 */
public class MailSenderProcess {

	// TODO: add code to deliver high priority mails first, normal priority mails after and low priority mails at a specified time
	public String sendMessages(int noMessages) {

		StringBuffer outputMessages = new StringBuffer();
		
//		MailSettings mailSettings = new MailSettings();

		// set mail session
		Session mailSession = null;

		String jndiName = new MailParameter("jndiName").getStringValue();
		String serverIp = new MailParameter("serverIp").getStringValue();
		int serverPort = new MailParameter("serverPort").getIntValue();

//		if (jndiName == null) {		
//			jndiName = mailSettings.getJndiName();
//		}
//
//		if (serverIp == null) {		
//			serverIp = mailSettings.getServerIp();
//		}
//		
//		if (serverPort == 0) {		
//			serverPort = mailSettings.getServerPort();
//		}
		
		if (jndiName != null && jndiName.length() > 0) {
			mailSession = getJndiSession(jndiName);
			Logger.debug(this, "[" + new Date().toString() + "] got jndi session for \"" + jndiName + "\"");
			outputMessages.append("[" + new Date().toString() + "] got jndi session for \"" + jndiName + "\"\n");
		} else if (serverIp != null && serverIp.length() > 0) {
			mailSession = getServerSession(serverIp, serverPort);
			Logger.debug(this, "[" + new Date().toString() + "] got server session for \"" + serverIp + ":" + serverPort + "\"");
			outputMessages.append("[" + new Date().toString() + "] got server session for \"" + serverIp + ":" + serverPort + "\"\n");
		} else {
			// TODO: throw exception
			Logger.error(this, "No mail settings defined");
			outputMessages.append("[" + new Date().toString() + "] No mail settings defined\n");
		}

		// if mail session
		if (mailSession != null) {
			
			// get messages to send
			MailMessageCollection messages = new MailMessageCollection(noMessages, MailMessage.STATE_NOT_SENT, new Date());

			// if messages to be send
			if (messages.getCollectionSize() > 0) {

				MailMessage mail = null;				

				for (int i = 0 ; i < messages.getCollectionSize() ; i++) {
					try {
						MimeMessage msg = new MimeMessage(mailSession);
						mail = messages.getMessage(i);						
						
						msg.setFrom(new InternetAddress(mail.getFrom()));
						msg.setSubject(mail.getSubject());

						if (mail.getTo().indexOf(',') >= 0) {
							Vector tos = new Vector();
							StringTokenizer strtok = new StringTokenizer(mail.getTo(), ",");
							while (strtok.hasMoreTokens()) {
								String str = strtok.nextToken();
								if (str != null) {
									str = str.trim();
									if (str.length() > 0) {
										tos.add(new InternetAddress(str));
									}
								}
							}
							InternetAddress[] toAddresses = (InternetAddress[])tos.toArray(new InternetAddress[0]);
							msg.setRecipients(Message.RecipientType.TO, toAddresses);
						} else {
							msg.setRecipients(Message.RecipientType.TO, mail.getTo());
						}

						if (mail.getCc() != null && mail.getCc().length() > 0) {
							if (mail.getCc().indexOf(',') >= 0) {
								Vector tos = new Vector();
								StringTokenizer strtok = new StringTokenizer(mail.getCc(), ",");
								while (strtok.hasMoreTokens()) {
									String str = strtok.nextToken();
									if (str != null) {
										str = str.trim();
										if (str.length() > 0) {
											tos.add(new InternetAddress(str));
										}
									}
								}
								InternetAddress[] ccAddresses = (InternetAddress[])tos.toArray(new InternetAddress[0]);
								msg.setRecipients(Message.RecipientType.CC, ccAddresses);
							} else {
								msg.setRecipients(Message.RecipientType.CC, mail.getCc());
							}	
						}

						if (mail.getBcc() != null && mail.getBcc().length() > 0) {
							if (mail.getBcc().indexOf(',') >= 0) {
								Vector tos = new Vector();
								StringTokenizer strtok = new StringTokenizer(mail.getBcc(), ",");
								while (strtok.hasMoreTokens()) {
									String str = strtok.nextToken();
									if (str != null) {
										str = str.trim();
										if (str.length() > 0) {
											tos.add(new InternetAddress(str));
										}
									}
								}
								InternetAddress[] bccAddresses = (InternetAddress[])tos.toArray(new InternetAddress[0]);
								msg.setRecipients(Message.RecipientType.BCC, bccAddresses);
							} else {
								msg.setRecipients(Message.RecipientType.BCC, mail.getBcc());
							}		
						}

						Multipart multipart = new MimeMultipart();

						// add body
						MimeBodyPart multipartHTML = new MimeBodyPart();
						multipartHTML.setContent(mail.getBody(), "text/html");
						multipart.addBodyPart(multipartHTML);

						// add attachments
						MailMessageAttachmentCollection attachments = new MailMessageAttachmentCollection(messages.getId(i));
						for (int indexAttachments = 0 ; indexAttachments < attachments.getCollectionSize() ; indexAttachments++) {
							final MailAttachment mailAttachment = new MailAttachment(attachments.getAttachmentId(indexAttachments));
							MimeBodyPart messageBodyPart = new MimeBodyPart();
							messageBodyPart.setDataHandler(new DataHandler(
								new DataSource() {
									public String getContentType() {
										return mailAttachment.getContentType();
									}
									public InputStream getInputStream() throws IOException {
										return new ByteArrayInputStream(mailAttachment.getContent().getBytes());
									}
									public String getName() {
										return new File(mailAttachment.getFilename()).getName();
									}
									public OutputStream getOutputStream() throws IOException {
										return null;
									}
								}));
							messageBodyPart.setFileName(new File(mailAttachment.getFilename()).getName());
							multipart.addBodyPart(messageBodyPart);
						}

						// sets message content and send it
						msg.setContent(multipart);
						Date sentDate = new Date();
						msg.setSentDate(sentDate);
						
						
						// double check to see if the message wasn't sent yet
						MailMessage mailCheck = new MailMessage(messages.getId(i));
						
						if (mailCheck.getState() == MailMessage.STATE_NOT_SENT) {
							Transport.send(msg);
							mail.setState(MailMessage.STATE_SENT);
							mail.setSentAt(sentDate);
							mail.update();
							Logger.debug(this, "[" + new Date().toString() + "] email sent to \"" + mail.getTo() + "\" with subject \"" + mail.getSubject() + "\"");
							outputMessages.append("[" + new Date().toString() + "] email sent to \"" + mail.getTo() + "\" with subject \"" + mail.getSubject() + "\"\n");
						} else {
							Logger.debug(this, "[" + new Date().toString() + "] email already sent to \"" + mail.getTo() + "\" with subject \"" + mail.getSubject() + "\"");
							outputMessages.append("[" + new Date().toString() + "] email already sent to \"" + mail.getTo() + "\" with subject \"" + mail.getSubject() + "\"\n");
						}
					} catch (AddressException aexc) {
						mail.setState(MailMessage.STATE_ERROR);
						mail.update();
						Logger.error(this, "Error setting address for mail id " + messages.getId(i) + ": " + aexc.getMessage());
						outputMessages.append("[" + new Date().toString() + "] error sending email to \"" + mail.getTo() + "\" with subject \"" + mail.getSubject() + "\": " + aexc.getMessage() + "\n");
						// TODO: throw exception
					} catch (MessagingException mexc) {
						mail.setState(MailMessage.STATE_ERROR);
						mail.update();
						Logger.error(this, "Error sending mail id " + messages.getId(i) + ": " + mexc.getMessage());
						outputMessages.append("[" + new Date().toString() + "] error sending email to \"" + mail.getTo() + "\" with subject \"" + mail.getSubject() + "\": " + mexc.getMessage() + "\n");
						// TODO: throw exception
					} catch (Exception exc) {
						mail.setState(MailMessage.STATE_ERROR);
						mail.update();
						Logger.error(this, "Exception when sending mail id " + messages.getId(i) + ": " + exc.getMessage());
						outputMessages.append("[" + new Date().toString() + "] error sending email to \"" + mail.getTo() + "\" with subject \"" + mail.getSubject() + "\": " + exc.getMessage() + "\n");
						// TODO: throw exception
					}
				}
			} else {
				Logger.debug(this, "[" + new Date().toString() + "] No mails to send");
				outputMessages.append("[" + new Date().toString() + "] No mails to send\n");	
			}
		}
		
		return outputMessages.toString();
	}

	private Session getJndiSession(String jndiName) {
		Session session = null;
		try {
			InitialContext ctx = new InitialContext();
			session =  (Session) ctx.lookup(jndiName);
		} catch (NamingException e) {
			try {
				InitialContext initialContext = new InitialContext();
				Context envContext = (Context)initialContext.lookup("java:comp/env");
				session = (Session) envContext.lookup(jndiName);
			} catch (NamingException nexc) {
				Logger.error(this, "NamingException when retreiving mail session from jndi name \"" + jndiName + "\": " + nexc.getMessage());
			} catch (Exception exc) {
				Logger.error(this, "Exception when retreiving mail session from jndi name \"" + jndiName + "\": " + exc.getMessage());
			}
		}
		return session;
	}

	private Session getServerSession(String host, int port) {
		Properties props = new Properties();
		props.put("mail.smtp.host", host);
		props.put("mail.smtp.port", new Integer(port));		
		return Session.getInstance(props, null);
	}

}
